4
文章转发自专业的Laravel开发者社区,原始链接:https://learnku.com/laravel/t...

如果你一直关注我的工作,你就会知道我推崇将我们的Laravel应用里面的工作更多地放到数据库层。 通过在数据库层完成更多工作,我们通常可以减少我们制造的数据库查询的数量,减少应用程序使用的内存量,并减少 Eloquent 处理模型所需的时间。 这可以带来一些非常显着的性能提升。

将更多工作推到数据库层的一种很好的方法是使用子查询。 子查询允许您在另一个数据库查询中运行嵌套查询。 当无法通过关系查询查到结果时,这是获取辅助模型数据的强大方法,而无需进行任何额外的数据库查询。 您还可以在 order by 语句, where 语句和其他数据库子句中使用子查询。

在 Laracon US 2019 talk期间, 我参考了我正在使用的查询构造器,这让我们在 laravel 中使用子查询变得更加简单. 我已经向 laravel 提交了三个 pr ,将这些添加到 laravel 的内核中.

概述:

“Select” 子查询

Pull request #29567 select()addSelect()查询构建器方法支持子查询

举个例子,假设我们有一个目的地表 destinations 和一个到目的地的航班表 flights flights 表包含一个 arrival_at 字段,表示航班何时到达目的地。

在 Laravel 6.0 中使用了新的子查询功能,比如要查询全部目的地 destinations,以及抵达各目的地最后一班飞机的信息 name ,我们可以用单条语句这样查询:

return Destination::addSelect(['last_flight' => Flight::select('name')
    ->whereColumn('destination_id', 'destinations.id')
    ->orderBy('arrived_at', 'desc')
    ->limit(1)
])->get();

注意这里是怎么使用 Eloquent 来生成的子查询。这样的语法更好,更具有直观表现力。同样的,你也可以使用 query builder:

return Destination::addSelect(['last_flight' => function ($query) {
    $query->select('name')
        ->from('flights')
        ->whereColumn('destination_id', 'destinations.id')
        ->orderBy('arrived_at', 'desc')
        ->limit(1);
}])->get();

“Order by” 子查询

此外, Pull request #29563 使我们可以在查询生成器的  orderBy() 方法中使用子查询。继续我们上面的示例,我们可以根据最后一班航班到达目的地的时间对目的地进行排序。

return Destination::orderByDesc(
    Flight::select('arrived_at')
        ->whereColumn('destination_id', 'destinations.id')
        ->orderBy('arrived_at', 'desc')
        ->limit(1)
)->get();

与选择一样,您也可以直接使用查询构建器来创建子查询。 例如,您可能希望根据用户的上次登录日期订购:

return User::orderBy(function ($query) {
    $query->select('created_at')
        ->from('logins')
        ->whereColumn('user_id', 'users.id')
        ->latest()
        ->limit(1);
})->get();

“From” 子查询

最后, Pull request #29602 使我们在查询构造器中的  from() 使用子查询成为可能。这些有时称为派生表。

例如,你可能想要计算应用程序中用户的平均捐赠总额。 但是,在SQL中,嵌套聚合函数是不可能的:

AVG(SUM(amount))

相反,我们可以使用from子查询来计算它:

return DB::table(function ($query) {
    $query->selectRaw('sum(amount) as total')
        ->from('donations')
        ->groupBy('user_id');
}, 'donations')->avg('total');

你可能不需要每天都用到这个,但是当你确实需要它的时候,它是必不可少的。

如果您在Laravel之外使用Eloquent,那么需要注意的一个突破性变化,在Illuminate/Database/Capsule/Manager 对象上 table() 方法有明显的改变。它已从表 table($table, $connection = null)更改为表table($table, $as = null, $connection = null)

了解更多

如果您有兴趣了解更多关于子查询和其他高级数据库技术的信息,请务必关注我的博客,并观看我的 Laracon US 2019 talk演讲。

在Laracon,我还发布了一个新的视频课程,我正在研究Eloquent的性能模式。本课程的目的是教Laravel开发人员如何把更多的工作推送到数据库层来大幅提高Laravel应用程序的性能,同时仍然使用Eloquent ORM。如果你感兴趣的话,一定要加入邮件列表!


summerblue
11k 声望15.4k 粉丝

刻意练习,每日精进